﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace FUSE
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class Ship : Microsoft.Xna.Framework.GameComponent
    {
        Texture2D shipTexture;

        Rectangle shipBox;


        //public Vector2 startPosition = new Vector2(288,465);
        public Vector2 startPosition = new Vector2(0, 0);

        //Boolean orbitBool;
        //Boolean crashBool;
        public Boolean inCollision;
        public Boolean collisionCourse;
        public Boolean inOrbit;
        public Boolean isLaunched;
        public Vector2 _velocity;
        private Vector2 position;
        private LinkedList<Vector2> orbitPath;

        public String currentSquareType;

        public Vector2 inMassedComponentSlingRadiusVectorFlag;
        public double inPlanetCrashRadius;
        public double angleFromPosX;
        public double launchAngle;

        private double playerInputTheta;
        public int accelerationX;
        public int accelerationY;
        public int accelerationRight;
        public int accelerationLeft;

        //Game attributes
        public double currentArmor = 0;
        public double currentHealth = 0;
        public double currentFuel = 0;
        public double currentSpeed = 0; //should be updated when velocity is calculated
        public Ship(Game game)
            : base(game)
        {

        }
        /// <summary>
        /// Set the ship conditions i.e. the starting stats
        /// </summary>
        /// <param name="gotMaxArmor"></param>
        /// <param name="gotMaxHealth"></param>
        /// <param name="gotMaxSpeed"></param>
        /// <param name="gotMaxFuel"></param>
        public void setShipConditions(double gotMaxArmor, double gotMaxHealth, double gotMaxSpeed, double gotMaxFuel)
        {
            currentArmor = gotMaxArmor;
            currentHealth = gotMaxHealth;
            currentSpeed = gotMaxSpeed;
            currentFuel = gotMaxFuel;
        }
        public Rectangle ShipBox
        {
            get { return shipBox; }
        }

        public void setOrbitPath(LinkedList<Vector2> newOrbitPath)
        {
            orbitPath = newOrbitPath;
        }

        public Vector2 Velocity
        {
            get { return _velocity; }
        }
        public Vector2 Position
        {
            get { return position; }
        }
        public void setPosition(float x, float y)
        {
            position.X = x;
            position.Y = y;
            shipBox.X = ((int)x) - (shipBox.Width / 2);
            shipBox.Y = ((int)y) - (shipBox.Height / 2);
        }
        public void setVelocity(float x, float y)
        {
            //Console.WriteLine("setVelocity(X=" + x + ",Y=" + y + ")");
            _velocity.X = x;
            _velocity.Y = y;

            this.updateAngle();
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here

            //orbitBool = false;
            //crashBool = false;

            inCollision = false;
            collisionCourse = false;
            inOrbit = false;
            isLaunched = false;

            _velocity = Vector2.Zero;
            position = startPosition;
            shipBox.X = (int)(startPosition.X - (shipBox.Width / 2));
            shipBox.Y = (int)(startPosition.Y - (shipBox.Height / 2));
            //position = new Vector2(50, 50);

            orbitPath = new LinkedList<Vector2>();
            currentSquareType = String.Empty;

            //angleFromPosX = 0;
            angleFromPosX = (Math.PI / 2);
            launchAngle = Math.PI/2; //90 degrees
            //angleFromPosX = Math.PI;
            //angleFromPosX = Math.PI + (Math.PI / 2);
            //angleFromPosX = (Math.PI * 2);

            inMassedComponentSlingRadiusVectorFlag = new Vector2(-1, -1);

            accelerationX = 1;
            accelerationY = 1;
            playerInputTheta = 0;

            base.Initialize();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here

            base.Update(gameTime);
        }

        public void LoadContent(Texture2D texture)
        {
            shipTexture = texture;
            shipBox = new Rectangle();
            shipBox.Width = texture.Width;
            shipBox.Height = texture.Height;
            shipBox.X = 0;
            shipBox.Y = 0;
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            Vector2 screenPos = new Vector2((Game.GraphicsDevice.Viewport.Width / 2),
                (Game.GraphicsDevice.Viewport.Height));
            Vector2 spriteCenter = new Vector2((shipBox.Width / 2), (shipBox.Height / 2));
            //Vector2 spriteCenter = new Vector2((shipBox.Width), (shipBox.Height));
            //Vector2 spriteCenter = new Vector2(0, 0);
            spriteBatch.Draw(shipTexture, position, null, Color.White, -(float)(angleFromPosX - Math.PI / 2), spriteCenter, 1f, SpriteEffects.None, 0f);

        }

        public void setRadianAngle(float radians)
        {
            double radius = Math.Sqrt(Math.Pow(this._velocity.X, 2) + Math.Pow(this._velocity.Y, 2));
            angleFromPosX = radians;

            this.normalizeAngleFromPosX();

            //quadrants are reversed in Xna
            //e.g Quadrant1 = Quandrant4, Quandrant2 = Quandrant3
            this._velocity.X = (float)(radius * Math.Cos(-angleFromPosX));
            this._velocity.Y = (float)(radius * Math.Sin(-angleFromPosX));

            this.updateAngle();
        }

        private void normalizeAngleFromPosX()
        {
            if (angleFromPosX >= 2 * Math.PI)
                angleFromPosX = angleFromPosX % (2 * Math.PI);
            else if (angleFromPosX < 0)
            {
                angleFromPosX = angleFromPosX % (2 * Math.PI);
                angleFromPosX = angleFromPosX + (2 * Math.PI);
            }
        }

        public void incrementRadianAngle()
        {
            double radius = Math.Sqrt(Math.Pow(this._velocity.X, 2) + Math.Pow(this._velocity.Y, 2));
            double degreeEquiv = (Math.PI / 2d) / 90d; //TODO: need to fix degree to radian equiv calc
            double degreeInterval = 5;

            angleFromPosX += (degreeEquiv * degreeInterval);

            this.normalizeAngleFromPosX();

            this._velocity.X = (float)(radius * Math.Cos(-angleFromPosX));
            this._velocity.Y = (float)(radius * Math.Sin(-angleFromPosX));

            this.updateAngle();
        }

        public void decrementRadianAngle()
        {
            double radius = Math.Sqrt(Math.Pow(this._velocity.X, 2) + Math.Pow(this._velocity.Y, 2));
            double degreeEquiv = (Math.PI / 2d) / 90d; //TODO: need to fix degree to radian equiv calc
            double degreeInterval = 5;

            angleFromPosX -= (degreeEquiv * degreeInterval);

            this.normalizeAngleFromPosX();

            this._velocity.X = (float)(radius * Math.Cos(-angleFromPosX));
            this._velocity.Y = (float)(radius * Math.Sin(-angleFromPosX));

            this.updateAngle();
        }

        

        public void updateTrajectoryAngle()
        {
            //calculate ship_Trajectory (normal angle of ship)
            //dVector = sqrt(x^2+y^2)
            updateAngle();
            //NOTE: must be in planet radius
            //calculate ship angle to Planet (angle of planet relative to ship)


            double xComponent = position.X - inMassedComponentSlingRadiusVectorFlag.X;
            double yComponent = position.Y - inMassedComponentSlingRadiusVectorFlag.Y;

            double distanceVector = Math.Sqrt((xComponent * xComponent + yComponent * yComponent));
            //TODO: calculate the angle which the player can turn
            playerInputTheta = 0;

            double planetToShipTheta = Math.Atan(Math.Abs(yComponent) / Math.Abs(xComponent));
            double planetThetaOuterBounds = Math.Atan(inPlanetCrashRadius / distanceVector);

            int planetToShipQuadrant = findQuadrant(xComponent, yComponent);
            int shipToPlanetQuadrant = planetToShipQuadrant + 2;
            if (shipToPlanetQuadrant > 4)
                shipToPlanetQuadrant = (planetToShipQuadrant + 2) % 4;
            
            double planetThetaFromX = -1;


            //TODO: FIX theta calculation
            if (shipToPlanetQuadrant == 1)
                planetThetaFromX = planetToShipTheta;
            else if (shipToPlanetQuadrant == 2)
                planetThetaFromX = Math.PI - planetToShipTheta;
            else if (shipToPlanetQuadrant == 3)
                planetThetaFromX = Math.PI + planetToShipTheta;
            else if (shipToPlanetQuadrant == 4)
                planetThetaFromX = (2 * Math.PI) - planetToShipTheta;
            else
                Console.WriteLine("Can't Find Ship quadrant in updateTrajectoryAngle()");

            //flip Theta to get ship's definite crash trajectory to planet
            //planetThetaFromX = (planetThetaFromX + Math.PI) % (2 * Math.PI);

            /*
            distanceVector = Math.Sqrt(this.Velocity.X * this.Velocity.X + this.Velocity.Y * this.Velocity.Y);
            xComponent = this.Velocity.X;
            yComponent = this.Velocity.Y;
            double shipTheta = Math.Atan(Math.Abs(yComponent/xComponent));//is this correct?
             */

            //Just use angle from Pos X to find out which way the ship is facing/heading towards

            /**
             * if(ship TrajectoryTheta is in between shipToPlanetTheta + upperBound(radiusPlanet)
             * and - lowerBound(radiusPlanet) && in orbitForce && playerInputTheta added wouldn't
             * change its collision course set collision course to true
             */
            if (currentSquareType.Equals("OrbitForce"))
            {

                double addedUpperBound = -1;
                double addedLowerBound = -1;
                if ((planetThetaFromX + planetThetaOuterBounds) > (2 * Math.PI))
                    addedUpperBound = (planetThetaFromX + planetThetaOuterBounds) % (2 * Math.PI);
                if ((planetThetaFromX - planetThetaOuterBounds) < 0)
                    addedLowerBound = Math.Abs((planetThetaFromX - planetThetaOuterBounds));


                if (((angleFromPosX + playerInputTheta) >= (planetThetaFromX - planetThetaOuterBounds))
                    && ((angleFromPosX + playerInputTheta) <= (planetThetaFromX + planetThetaOuterBounds)))
                    collisionCourse = true;//ie turn off orbit
                else if (addedUpperBound != -1)
                {
                    if (((angleFromPosX + playerInputTheta) >= 0) && ((angleFromPosX + playerInputTheta) <= addedUpperBound))
                    {
                        collisionCourse = true;
                    }
                }
                else if (addedLowerBound != -1)
                {
                    if (((angleFromPosX + playerInputTheta) >= (-addedLowerBound)) && ((angleFromPosX + playerInputTheta) <= 0))
                    {
                        collisionCourse = true;
                    }
                }
                else
                    collisionCourse = false;
            }
        }

        private int findQuadrant(double xCoord, double yCoord)
        {
            if ((xCoord > 0) && (yCoord <= 0))
                return 1;
            else if ((xCoord <= 0) && (yCoord < 0))
                return 2;
            else if ((xCoord < 0) && (yCoord >= 0))
                return 3;
            else if ((xCoord >= 0) && (yCoord > 0))
                return 4;
            else
                return 0;
        }


        public void updateAngle()
        {
            double distanceVector = Math.Sqrt(this.Velocity.X*this.Velocity.X + this.Velocity.Y*this.Velocity.Y);
            double xComponent = this.Velocity.X;
            double yComponent = this.Velocity.Y;

            if ((xComponent == 0) && (yComponent == 0))
            {
                //Do nothing
            }
            //Quadrant 1
            else if (xComponent > 0 && yComponent <= 0)
            {
                //double angleCheck = Math.Acos(xComponent / distanceVector);
                angleFromPosX = Math.Abs(Math.Acos(xComponent / distanceVector));
            }
            //Quadrant 2
            else if (xComponent <= 0 && yComponent < 0)
            {
                //double angleCheck = Math.Asin(yComponent / distanceVector);
                angleFromPosX = Math.PI - Math.Abs(Math.Asin(yComponent / distanceVector));

            }
            //Quadrant 3
            else if (xComponent < 0 && yComponent >= 0)
            {
                //double angleCheck = Math.Acos(xComponent / distanceVector);
                angleFromPosX = (2 * Math.PI) - Math.Abs(Math.Acos(xComponent / distanceVector));
            }
            //Quadrant 4
            else if (xComponent >= 0 && yComponent > 0)
            {
                //double angleCheck = Math.Asin(yComponent / distanceVector);
                angleFromPosX = (2 * Math.PI) - Math.Abs(Math.Asin(yComponent / distanceVector));
            }
           
        }

        public void setInPlanetSlingRadiusVectorFlag(int x, int y)
        {
            inMassedComponentSlingRadiusVectorFlag.X = x;
            inMassedComponentSlingRadiusVectorFlag.Y = y;
        }

        public Vector2 StartPosition
        {
            set { startPosition = value; }
        }
    }
}
